package com.skynet.supervision.utils;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;

import javax.annotation.Nonnull;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Objects;


@SuppressWarnings({"UnusedReturnValue", "unused"})
public abstract class ReflectUtils extends ReflectionUtils {


    @SuppressWarnings("unchecked")
    @SneakyThrows
    public static <T> T constructDefault(Class<?> clazz) {
        Constructor<?> defaultConstructor = Objects.requireNonNull(clazz)
                .getDeclaredConstructor();
        makeAccessible(defaultConstructor);
        return (T) defaultConstructor.newInstance();
    }

    /**
     * 构建泛型class
     *
     * @param genericClazz  泛型class
     * @param genericParams 泛型class的参数列表
     * @return JavaType
     */
    public static JavaType buildGenericType(@Nonnull ApplicationContext applicationContext,
                                            @Nonnull Class<?> genericClazz, Class<?>... genericParams) {
        return applicationContext.getBean(ObjectMapper.class).getTypeFactory()
                .constructParametricType(genericClazz, genericParams);
    }

    /**
     * 构建泛型class
     *
     * @param genericClazz 泛型class
     * @param javaType     较难描述的类型,使用JavaType进行封装
     * @return JavaType
     */
    public static JavaType buildGenericType(@Nonnull ApplicationContext applicationContext,
                                            @Nonnull Class<?> genericClazz, JavaType... javaType) {
        return applicationContext.getBean(ObjectMapper.class).getTypeFactory().constructParametricType(genericClazz, javaType);
    }

    public static Object invokeMethodSafe(@Nonnull Method method, @Nonnull Object object) {
        makeAccessible(method);
        return invokeMethod(method, object);
    }

    public static Object invokeMethodSafe(@Nonnull Method method, @Nonnull Object object, Object... params) {
        makeAccessible(method);
        return invokeMethod(method, object, params);
    }
}
